package com.perceptivesoftware.licensing.server.util;

import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.servlet.ServletContext;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.ServletContextAware;
import com.perceptivesoftware.licensing.client.common.util.Utils;
import com.perceptivesoftware.licensing.server.user.UserSessionTracker;

@Service
public class PropertiesUtil implements ServletContextAware
{
	Logger logger = Logger.getLogger(getClass());

	@Autowired
	private UserSessionTracker userSessionTracker;

	private String versionString;
	private static ServletContext servletContext;
	
	public void dumpVersionInfo(boolean isStartup)
	{
		// save to list then output all at once in case calls themselves
		// generate output (e.g. getCurrentlyLoggedInUserCount())
		final ArrayList<String> output = new ArrayList<String>();
		if (isStartup)
		{
			output.add(">");
			output.add(">");
		}
		output.add(">");
		output.add(">");
		if (isStartup)
		{
			output.add("Perceptive Licensing Server Startup");
		}
		else
		{
			output.add("Perceptive Licensing Information");
		}
		output.add("Perceptive Licensing: " + getVersionString());
		if (servletContext != null)
		{
			// servletContext will be null in JUnit testing
			output.add("Tomcat: " + servletContext.getServerInfo());
		}
		final RuntimeMXBean RuntimemxBean = ManagementFactory.getRuntimeMXBean();
		final List<String> argumentsList = RuntimemxBean.getInputArguments();
		final String argumentsString = Utils.implode(argumentsList, " ");
		final int availableProcessors = Runtime.getRuntime().availableProcessors();
		output.add("Currently logged in users:" + getCurrentlyLoggedInUserCount());
		output.add("Java: " + System.getProperty("java.version") + " x" + System.getProperty("sun.arch.data.model", "?") + " (from " + System.getProperty("java.vendor") + ")");
		output.add("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version"));
		output.add("Processors: " + availableProcessors);
		output.add("Command line arguments:" + argumentsString);
		output.add("Temp Dir: " + System.getProperty("java.io.tmpdir"));
		output.add(">");
		output.add(">");
		if (isStartup)
		{
			output.add(">");
			output.add(">");
		}
		/*
		 * if (availableProcessors < 4) { output.add("***"); output.add("***");
		 * output.add("WARNING: You are currently running NolijWeb on a server with fewer than the minimum number of CPU cores. You currently have " +
		 * availableProcessors + " cores, Nolij Web requires a minimum of 4 cores, 8 recommended."); output.add("***"); output.add("***"); } else if
		 * (availableProcessors < 8) { output.add("***"); output.add(
		 * "You are currently running NolijWeb on a server with fewer than the recommended number of CPU cores (but meet the minumum requirements). You currently have "
		 * + availableProcessors + " cores, Nolij Web requires a minimum of 4 cores, 8 recommended."); output.add("***"); } if
		 * (!argumentsString.contains("-XX:MaxPermSize=")) { output.add("***");
		 * output.add("You did not specify a MaxPermSize in your JVM Arguments. Add -XX:MaxPermSize=256m to your JVM Arguments"); output.add("***"); }
		 */
		if (!argumentsString.contains("-Xmx"))
		{
			output.add("***");
			output.add("You did not specify a max memory setting (Xmx) in your JVM Arguments. Add -Xmx4096m to your JVM Arguments (scaled to the amount of available RAM; 4096m minimum, 8192m recommended)");
			output.add("***");
		}
		else
		{
			//final String memStr = argumentsString.split(Pattern.quote("-Xmx"))[1].split("m")[0];
			try
			{
				/*
				 * final Integer i = Integer.parseInt(memStr); if (i < 4000) { output.add("***"); output.add("***");
				 * output.add("WARNING: You are currently running NolijWeb with less than the minimum amount of memory. You currently have " + i +
				 * "m , Nolij Web requires a minimum of 4096m, 8192m recommended."); output.add("***"); output.add("***"); } else if (i < 8000) {
				 * output.add("***"); output.add(
				 * "Note: You are currently running NolijWeb with less than the recommended amount of memory (but meet the minimum requirements). You currently have "
				 * + i + "m , Nolij Web recommends 8192m."); output.add("***"); }
				 */
			}
			catch (final NumberFormatException nfe)
			{
				output.add("***");
				output.add("***");
				output.add("***");
				output.add("WARNING: -Xmx setting could not be parsed. Expected format: -Xmx8192m");
				output.add("***");
				output.add("***");
				output.add("***");
			}
		}
		for (final String s : output)
		{
			logger.info(s);
		}
	}

	private int getCurrentlyLoggedInUserCount()
	{
		return userSessionTracker.getLoggedInUserCount();
	}

	@Override
	public void setServletContext(ServletContext sc)
	{
		servletContext = sc;
	}

	public String getVersionString()
	{
		if (versionString == null)
		{
			try
			{
				versionString = getManifestValue("Implementation-Version") + "_b" + getManifestValue("Implementation-Build");
			}
			catch (final ManifestNotAvailableException e)
			{
				logger.warn("Manifest file not available, could not report version string");
			}
			if (versionString == null || versionString.equals("null_bnull"))
			{
				versionString = "VersionNotAvailable";
			}
		}
		return versionString;
	}
	
	public static String getProjectName()
	{
		return servletContext.getContextPath();
	}

	private String getManifestValue(String key) throws ManifestNotAvailableException
	{
		final Properties prop = new Properties();
		final InputStream resourceAsStream = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF");
		if (resourceAsStream == null)
		{
			throw new ManifestNotAvailableException("Manifest file not available");
		}
		try
		{
			prop.load(resourceAsStream);
		}
		catch (final IOException e1)
		{
			throw new ManifestNotAvailableException("Manifest file not available");
		}
		return prop.getProperty(key, null);
	}

	public class ManifestNotAvailableException extends Exception
	{
		private static final long serialVersionUID = 4560323310772564782L;

		public ManifestNotAvailableException(String string, Exception e)
		{
			super(string, e);
		}

		public ManifestNotAvailableException(String string)
		{
			super(string);
		}
	}
}